﻿using SAIM.Models.DAL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Web;

namespace SAIM.Models.BLL
{
    public class NotificationsBLL : IDisposable
    {
        private SAIMEntities db;
        private List<string> Users { get; set; }

        public NotificationsBLL(List<string> users)
        {
            db = new SAIMEntities();
            Users = users;
        }

        public NotificationsBLL()
        {
            db = new SAIMEntities();
            Users = new List<string>();
        }

        /// <summary>
        /// Agrega una nueva notificación de creación de ticket
        /// </summary>
        /// <param name="model">Modelo del cual se toma la información del ticket</param>
        /// <param name="url">Url a la que dirigira la notificación</param>
        /// <param name="Id">Id del usuario que desencadena la notificación</param>
        /// <returns></returns>
        public bool NewTicket(NewTicketsViewModel model, string url, string Id)
        {
            string ticketSubject = string.Empty;

            try
            {
                #region Creación de la notificación
                foreach (System.Web.Mvc.SelectListItem item in model.Issues)
                {
                    if (item.Value == model.Issue.ToString())
                        ticketSubject = item.Text;
                }
                NotificationsDAL notification = new NotificationsDAL()
                {
                    Id = Hash(url + "/" + ticketSubject + "/" + model.Details),
                    NotificationDate = DateTime.Now,
                    Details = "El usuario " + model.User + " creó un nuevo ticket." +
                                "<br/><b>Servicio:</b> " + ticketSubject +
                                "<br/><b>Detalles:</b> " + model.Details,
                    Url = url
                };
                db.Notifications.Add(notification);
                db.SaveChanges();
                #endregion
                #region Relación usuarios - notificación
                foreach (string userId in Users)
                {
                    AspNetUsersDAL user = db.AspNetUsers.FirstOrDefault(u => u.Id == userId);
                    if (user != null && user.Id != Id)
                    {
                        UsersNotificationsDAL relationship = new UsersNotificationsDAL()
                        {
                            Active = true,
                            AspNetUsers = user,
                            Notifications = notification
                        };
                        db.UsersNotifications.Add(relationship);
                    }
                };
                db.SaveChanges();
                #endregion
                return true;
            }
            catch (Exception) { return false; }
        }

        /// <summary>
        /// Agrega una nueva notificación de creación de comentario en ticket
        /// </summary>
        /// <param name="model">Modelo del cual se toma la información del comentario</param>
        /// <param name="url">Url a la que dirigira la notificación</param>
        /// <param name="Id">Id del usuario que desencadena la notificación</param>
        /// <returns></returns>
        public bool NewTicketComment(TicketComment model, string url, string Id)
        {
            try
            {
                #region Creación de la notificación
                NotificationsDAL notification = new NotificationsDAL()
                {
                    Id = Hash(url + "/" + model.UserName + "/" + model.Comment),
                    NotificationDate = DateTime.Now,
                    Details = "El usuario " + model.UserName + " <b>publicó un comentario</b> en el ticket número " + model.IdTicket + ".",
                    Url = url 
                };
                db.Notifications.Add(notification);
                db.SaveChanges();
                #endregion
                #region Relación usuarios - notificación
                foreach (string userId in Users)
                {
                    AspNetUsersDAL user = db.AspNetUsers.FirstOrDefault(u => u.Id == userId);
                    if (user != null && user.Id != Id)
                    {
                        UsersNotificationsDAL relationship = new UsersNotificationsDAL()
                        {
                            Active = true,
                            AspNetUsers = user,
                            Notifications = notification
                        };
                        db.UsersNotifications.Add(relationship);
                    }
                };
                db.SaveChanges();
                #endregion
                return true;
            }
            catch (Exception) { return false; }
        }

        /// <summary>
        /// Agrega una nueva notificación de edición de comentario en ticket
        /// </summary>
        /// <param name="model">Modelo del cual se toma la información del comentario</param>
        /// <param name="url">Url a la que dirigira la notificación</param>
        /// <param name="Id">Id del usuario que desencadena la notificación</param>
        /// <returns></returns>
        public bool TicketCommentEdition(TicketComment model, string url, string Id)
        {
            try
            {
                #region Creación de la notificación
                NotificationsDAL notification = new NotificationsDAL()
                {
                    Id = Hash(url + "/" + model.UserName + "/" + model.Comment),
                    NotificationDate = DateTime.Now,
                    Details = "El usuario " + model.UserName + " <b>editó un comentario</b> en el ticket número " + model.IdTicket + ".",
                    Url = url
                };
                db.Notifications.Add(notification);
                db.SaveChanges();
                #endregion
                #region Relación usuarios - notificación
                foreach (string userId in Users)
                {
                    AspNetUsersDAL user = db.AspNetUsers.FirstOrDefault(u => u.Id == userId);
                    if (user != null && user.Id != Id)
                    {
                        UsersNotificationsDAL relationship = new UsersNotificationsDAL()
                        {
                            Active = true,
                            AspNetUsers = user,
                            Notifications = notification
                        };
                        db.UsersNotifications.Add(relationship);
                    }
                };
                db.SaveChanges();
                #endregion
                return true;
            }
            catch (Exception) { return false; }
        }

        /// <summary>
        /// Agrega una nueva notificación. Archivo adjuntado en ticket
        /// </summary>
        /// <param name="userName">Nombre de usuario que adjunta el archivo</param>
        /// <param name="url">Url de los detalles del ticket</param>
        /// <param name="Id">ID de usuario que adjunta el archivo</param>
        /// <param name="ticketId">ID de  ticket</param>
        /// <returns></returns>
        public bool FileUploaded(string userName, string url, string Id, string ticketId)
        {
            try
            {
                #region Creación de la notificación
                NotificationsDAL notification = new NotificationsDAL()
                {
                    Id = Hash(url + "/" + userName + "/FileUpload" + Id),
                    NotificationDate = DateTime.Now,
                    Details = "El usuario " + userName + "  <b>adjuntó uno o más archivos</b> en el ticket número" + ticketId + ".",
                    Url = url
                };
                db.Notifications.Add(notification);
                db.SaveChanges();
                #endregion
                #region Relación usuarios - notificación
                foreach (string userId in Users)
                {
                    AspNetUsersDAL user = db.AspNetUsers.FirstOrDefault(u => u.Id == userId);
                    if (user != null && user.Id != Id)
                    {
                        UsersNotificationsDAL relationship = new UsersNotificationsDAL()
                        {
                            Active = true,
                            AspNetUsers = user,
                            Notifications = notification
                        };
                        db.UsersNotifications.Add(relationship);
                    }
                };
                db.SaveChanges();
                #endregion
                return true;
            }
            catch (Exception) { return false; }
        }

        /// <summary>
        /// Agrega una notificación. Nuevo usuario colaborador en ticket
        /// </summary>
        /// <param name="userName">Nombre de usuario que se agrega al ticket</param>
        /// <param name="ticketId">ID de ticket</param>
        /// <param name="url">Url de los detalles del ticket</param>
        /// <param name="Id">ID de usuario que agrega al colaborador</param>
        /// <returns></returns>
        public bool TicketUserAdded(string userName, string ticketId, string url, string Id)
        {
            try
            {
                #region Creación de la notificación
                NotificationsDAL notification = new NotificationsDAL()
                {
                    Id = Hash(url + "/" + userName + "/UserAdded" + Id),
                    NotificationDate = DateTime.Now,
                    Details = "El usuario " + userName + "  <b>fue agregado como usuario colaborador</b> en el ticket número " + ticketId + ".",
                    Url = url
                };
                db.Notifications.Add(notification);
                db.SaveChanges();
                #endregion
                #region Relación usuarios - notificación
                foreach (string userId in Users)
                {
                    AspNetUsersDAL user = db.AspNetUsers.FirstOrDefault(u => u.Id == userId);
                    if (user != null && user.Id != Id)
                    {
                        UsersNotificationsDAL relationship = new UsersNotificationsDAL()
                        {
                            Active = true,
                            AspNetUsers = user,
                            Notifications = notification
                        };
                        db.UsersNotifications.Add(relationship);
                    }
                };
                db.SaveChanges();
                #endregion
                return true;
            }
            catch (Exception) { return false; }
        }

        /// <summary>
        /// Agrega una notificación. Usuario colaborador eliminado de ticket
        /// </summary>
        /// <param name="userName">Nombre de usuario que se agrega al ticket</param>
        /// <param name="ticketId">ID de ticket</param>
        /// <param name="url">Url de los detalles del ticket</param>
        /// <param name="Id">ID de usuario que agrega al colaborador</param>
        /// <returns></returns>
        public bool TicketUserRemoved(string userName, string ticketId, string url, string Id)
        {
            try
            {
                #region Creación de la notificación
                NotificationsDAL notification = new NotificationsDAL()
                {
                    Id = Hash(url + "/" + userName + "/UserAdded" + Id),
                    NotificationDate = DateTime.Now,
                    Details = "El usuario " + userName + "  <b>fue eliminado de los usuarios colaboradores</b> en el ticket número " + ticketId + ".",
                    Url = url
                };
                db.Notifications.Add(notification);
                db.SaveChanges();
                #endregion
                #region Relación usuarios - notificación
                foreach (string userId in Users)
                {
                    AspNetUsersDAL user = db.AspNetUsers.FirstOrDefault(u => u.Id == userId);
                    if (user != null && user.Id != Id)
                    {
                        UsersNotificationsDAL relationship = new UsersNotificationsDAL()
                        {
                            Active = true,
                            AspNetUsers = user,
                            Notifications = notification
                        };
                        db.UsersNotifications.Add(relationship);
                    }
                };
                db.SaveChanges();
                #endregion
                return true;
            }
            catch (Exception) { return false; }
        }   

        /// <summary>
        /// Devuelve el numero total de notificaciones activas para un usario específico
        /// </summary>
        /// <param name="userId">ID de usuario</param>
        /// <returns></returns>
        public int GetUserNotifications(string userId)
        {
            int userNotifications = (from userNotification in db.UsersNotifications where userNotification.UserId == userId && userNotification.Active == true select userNotification).ToList().Count;
            return userNotifications;
        }

        /// <summary>
        /// Devuelve las notificaciones para un usuario específico
        /// Utilizado para cargar datos en BootGrid
        /// </summary>
        /// <param name="userId">ID de usuario</param>
        /// <param name="current">Página actual del grid</param>
        /// <param name="rowCount">Elementos por página</param>
        /// <param name="total">Número total de elementos encontrados (Referencia)</param>
        /// <returns></returns>
        public List<UsersNotificationsDAL> GetUserNotifications(string userId, ref int current, int rowCount, ref int total)
        {
            List<UsersNotificationsDAL> notifications = (from userNotification in db.UsersNotifications where userNotification.UserId == userId select userNotification).ToList();
            // Total de elementos
            total = notifications.Count;
            // Ordenamiento por fecha
            notifications.Sort((n2, n1) => n1.Notifications.NotificationDate.CompareTo(n2.Notifications.NotificationDate));
            // Filtro mediante el número de página y los elementos por página
            if ((current - 1) * rowCount >= total)
                current -= 1;
            if (total > 0)
            {
                notifications = notifications.GetRange(
                    (current - 1) * rowCount,
                    (((current - 1) * rowCount) + rowCount) <= total ? rowCount : total - ((current - 1) * rowCount));
            }
            return notifications;
        }

        /// <summary>
        /// Marca todas las notificaciones de usuario como leídas
        /// </summary>
        /// <param name="userId">ID de usuario</param>
        /// <returns></returns>
        public bool ReadAll(string userId)
        {
            try
            {
                List<UsersNotificationsDAL> notifications = (from userNotification in db.UsersNotifications where userNotification.UserId == userId select userNotification).ToList();
                foreach(UsersNotificationsDAL element in notifications)
                {
                    element.Active = false;
                }
                db.SaveChanges();
            }
            catch (Exception) { return false; }
            return true;
        }

        /// <summary>
        /// Devuelve una notificación de usuario específica
        /// </summary>
        /// <param name="userId">ID de usuario de la notificación</param>
        /// <param name="notificationId">ID de notificación</param>
        /// <returns></returns>
        public Notification GetUserNotification(string userId, string notificationId)
        {
            try
            {
                UsersNotificationsDAL notification = db.UsersNotifications.FirstOrDefault(n => (n.UserId == userId) && (n.NotificationId == notificationId));
                if (notification != null)
                    return new Notification()
                    {
                        Date = notification.Notifications.NotificationDate.ToString(),
                        Detail = notification.Notifications.Details,
                        Url = notification.Notifications.Url,
                        status = (notification.Active != null && notification.Active == true) ? 1 : -1
                    };
                else
                    throw new HttpException(403, "Invalid Operation");
            }
            catch (Exception) { throw new HttpException(403, "Invalid Operation"); }
        }

        /// <summary>
        /// Marca una notificación de usuario como leída
        /// </summary>
        /// <param name="userId">ID de usuario de la notificación</param>
        /// <param name="notificationId">ID de notificación</param>
        /// <returns></returns>
        public bool ReadUserNotification(string userId, string notificationId)
        {
            try 
            {
                UsersNotificationsDAL notification = db.UsersNotifications.FirstOrDefault(n => (n.UserId == userId) && (n.NotificationId == notificationId));
                notification.Active = false;
                db.SaveChanges();
            }
            catch (Exception) { return false; }
            return true;
        }

        /// <summary>
        /// Elimina una notificación de usuario.
        /// </summary>
        /// <param name="userId">ID de usuario</param>
        /// <param name="notificationId">Id de notificación</param>
        /// <returns></returns>
        public bool RemoveUserNotification(string userId, string notificationId)
        {
            try
            {
                // Busqueda de la notificación
                UsersNotificationsDAL notification = db.UsersNotifications.FirstOrDefault(n => (n.UserId == userId) && (n.NotificationId == notificationId));
                // Se obtiene el número total de usuarios restantes para esta notificacion
                int usersByNotification = (from n in db.UsersNotifications where (n.UserId != userId) && (n.NotificationId == notificationId) select n).Count();
                // Eliminación de la notificación de usuario
                db.UsersNotifications.Remove(notification);
                // Si no existen usuarios asociados a la notificación se elimina la misma
                if (usersByNotification == 0)
                    db.Notifications.Remove((from n in db.Notifications where n.Id == notificationId select n).First());
                db.SaveChanges();
            }
            catch (Exception) { return false; }
            return true;
        }

        private string Hash(string fileName)
        {
            //create salt
            byte[] salt;
            new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);

            //create hash
            var pbkdf2 = new Rfc2898DeriveBytes(DateTime.UtcNow.ToLongDateString() + fileName, salt);
            var hash = pbkdf2.GetBytes(16);

            //combine salt and hash
            var hashBytes = new byte[32];
            Array.Copy(salt, 0, hashBytes, 0, 16);
            Array.Copy(hash, 0, hashBytes, 16, 16);

            //convert to base64
            var base64Hash = Convert.ToBase64String(hashBytes);

            return Regex.Replace(base64Hash, "[^A-Za-z0-9]", String.Empty);
        }

        public void Dispose()
        {
            db.Dispose();
        }


    }
}